from django.shortcuts import render
from django.views import View
from apps.users.models import Address
from meiduo_demo_1.utils.view import LoginRequiredMixin
from apps.goods.models import SKU
from .models import OrderInfo,OrderGoods
from django import http
from django.utils import timezone
from apps.carts.utils import get_redis_cart
import json
from decimal import Decimal
from django_redis import get_redis_connection
from django.db import transaction

# Create your views here.


class OrderCommitView(LoginRequiredMixin, View):
    """订单提交"""

    # @transaction.atomic # 当前post视图函数中所有的数据库操作即成一个事务
    # 此种装饰2个缺点：1、范围太大；2、无法执行回滚节点

    def post(self,request):
        """保存订单信息和订单商品信息"""

        # 1.提取参数
        data = json.loads(request.body.decode())
        address_id = data.get('address_id')
        pay_method = data.get('pay_method')

        # 1.1 参数校验
        if not all([address_id, pay_method]):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '参数缺失'
            })

        if pay_method not in [1, 2]:
            return http.JsonResponse({'code': 400, 'errmsg': '未知支付方式！'})

        if not isinstance(address_id, int):
            return http.JsonResponse({'code': 400, 'errmsg': '参数有误！'})

        # 2、新建OrderInfo
        # order_id订单主键，自己构造：(1)、不同的用户必须唯一 （2）针对同一个用户也是唯一
        # 针对需求(1)，可以把用户的id拼接；针对需求(2)拼接当前生成订单的时间戳字符串
        # 20200630160756000002
        # timezone.now() # 返回0时区表示的当前时间点对象
        # timezone.localtime() # 返回当前时区表示的当前时间点对象
        local_time = timezone.localtime()

        # 20200630160756 ---->  年月日时分秒 ----> "%Y%m%d%H%M%S"
        # 200630160756 ---->  年月日时分秒 ----> "%y%m%d%H%M%S"

        # 将时间对象按照指定的格式输出成字符串
        local_time_str = local_time.strftime("%Y%m%d%H%M%S")

        # 拼接时间字符串和用户id 生成唯一的订单id
        order_id = local_time_str + '%06d' % request.user.id

        # 3.1 从用户的购物车中提取sku商品数据
        # redis_cart= {b'14': b'5'}
        # redis_selected = {b'14'}
        redis_cart, redis_selected = get_redis_cart(request)

        # 开启事务
        with transaction.atomic():
            # 记录一个事务执行的节点
            save_id = transaction.savepoint()

            order = OrderInfo(
                order_id=order_id,
                user = request.user,
                address_id=address_id,
                total_count=0, # 先初始化为0，后续新增sku订单商品之后再修改
                total_amount=0,
                freight=Decimal('10.0'),
                pay_method=pay_method
            )

            # 后续新建订单商品从表数据之前，必须先把主表数据创建出来
            order.save()

            # 3. 新建OrderGoods
            # 3.2 根据选中的sku 商品 ，添加到0rderGoods
            # skus = SKU.objects.filter(id__in=redis_selected)
            carts = {}
            for sku_id in redis_selected:
                carts[int(sku_id)] = int(redis_cart[sku_id])
            sku_ids = carts.keys()

            for sku_id in sku_ids:
                # 增加的代码: 增加一个死循环
                while True:
                    # 查询SKU信息
                    sku = SKU.objects.get(pk=sku_id)

                    # 增加的代码：读取原始库存
                    old_stock = sku.stock
                    old_sales = sku.sales

                    # 判断SKU库存
                    count = carts[sku.id]
                    if count > sku.stock:
                        transaction.savepoint_rollback(save_id)
                        return http.JsonResponse({
                            'code': 400,
                            'errmsg': '库存不足'
                        })
                    # 增加的代码：乐观锁更新库存和销量
                    # 计算差值
                    new_stock = old_stock - count
                    new_sales = old_sales + count
                    result = SKU.objects.filter(
                        id=sku_id,
                        stock=old_stock
                    ).update(stock=new_stock, sales=new_sales)

                    # 若果下单失败，但是库存足够时
                    # 继续下单，知道下单成功或者库存不足为止
                    if result == 0:
                        continue


                    # sku:模型类对象
                    # 每在购物车商品数据中遍历出一个sku，就需要在订单商品中间表中插入一条数据
                    # sku.id = 14 --> b'14'
                    # sku_id = str(sku.id).encode()  # b'14'
                    # count = int(redis_cart[sku_id]) # b'5'

                    order_goods = OrderGoods(
                        order = order,
                        sku=sku,
                        count = count,
                        price=sku.price
                    )

                    # # 销量累加，库存减少
                    # sku.sales += count
                    # sku.stock -= count

                    # 同类spu销量增加
                    spu = sku.goods
                    spu.sales += count
                    spu.save()

                    # 订单的总商品数量累加
                    order.total_count +=count
                    order.total_amount += count * sku.price

                    # 插入订单商品表数据
                    order_goods.save()

                    # 增加的代码：
                    #下单成功或者失败就跳出循环
                    break

            # 加运费
            order.total_amount += order.freight
            order.save()

            # 清除保存点
            transaction.savepoint_commit(save_id)

        # 下单成功，需要把用户购物车商品去除
        conn = get_redis_connection('carts')
        for sku_id in redis_selected:
            conn.hdel('carts_%s' % request.user.id, sku_id)
            conn.srem('selected_%s' % request.user.id, sku_id)

        # 4 返回
        return http.JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'order_id': order_id
        })





class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""

    def get(self,request):
        """提供订单结算页面"""
        # 用户进入结算页面，把用户数据返回，用户生成订单
        user = request.user

        # 1.用户可选地址
        address_queryset = Address.objects.filter(
            user=user,
            is_deleted=False
        )
        addresses = []
        for address in address_queryset:
            # address 是一个地址模型类对象
            addresses.append({
                'id': address.id,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'receiver': address.receiver
            })

        # 2.当前用户购物车数据
        #2.1 从redis购物车中获取选中的sku_id
        # redis_cart = {b'14': b'5'}
        # redis_selected = {b'14'}
        redis_cart, redis_selected = get_redis_cart(request)

        # 2.2 进一步获取sku模型类对象——只取选中的sku
        skus = []
        for sku_id in redis_selected:
            sku = SKU.objects.get(pk=sku_id)
            # 2.3 构建skus 响应参数
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image_url,
                'price': sku.price,
                'count': int(redis_cart[sku_id])
            })
        # 在计算机中，浮点数取的是近似值
        # freight = 3.3 直接保存float类型数据会损失精读
        freight = Decimal('10.0')  # Decimal是用来记录十进制数值，是一种数值在python中的精确表达

        # 3、构建响应数据
        return http.JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'context': {
                'addresses': addresses,
                'skus': skus,
                'freight': freight
            }
        })